/*
 *  File.c
 *  jscsh
 *
 *  Created by Jonathan Hohle on 1/10/08.
 *  Copyright 2008 hohle.net. All rights reserved.
 *
 */

#include "File.h"
#include "util.h"
#include <sys/stat.h>
#include <ctype.h>

void File_extendHost(JSContextRef context, JSObjectRef contextGlobalObject)
{
    JSStaticFunction staticFunctions[] = {
        {"atime", File_s_atime, kJSPropertyAttributeNone},
        {"isBlockDev", File_s_isBlockDev, kJSPropertyAttributeNone},
        {"isCharDev", File_s_isCharDev, kJSPropertyAttributeNone},
        {"chmod", File_s_chmod, kJSPropertyAttributeNone},
        {"chown", File_s_chown, kJSPropertyAttributeNone},
        {"ctime", File_s_ctime, kJSPropertyAttributeNone},
        {"isDirectory", File_s_isDirectory, kJSPropertyAttributeNone},
        {"exists", File_s_exists, kJSPropertyAttributeNone},
        {"isFile", File_s_isFile, kJSPropertyAttributeNone},
        {"fileType", File_s_fileType, kJSPropertyAttributeNone},
        {"link", File_s_link, kJSPropertyAttributeNone},
        {"move", File_s_move, kJSPropertyAttributeNone},
        {"mtime", File_s_mtime, kJSPropertyAttributeNone},
        {"isPipe", File_s_isPipe, kJSPropertyAttributeNone},
        {"isSetGID", File_s_isSetGID, kJSPropertyAttributeNone},
        {"isSetUID", File_s_isSetUID, kJSPropertyAttributeNone},
        {"size", File_s_size, kJSPropertyAttributeNone},
        {"isSocket", File_s_isSocket, kJSPropertyAttributeNone},
        {"isSticky", File_s_isSticky, kJSPropertyAttributeNone},
        {"symlink", File_s_symlink, kJSPropertyAttributeNone},
        {"isSymlink", File_s_isSymlink, kJSPropertyAttributeNone},
        {"truncate", File_s_truncate, kJSPropertyAttributeNone},
        {"utime", File_s_utime, kJSPropertyAttributeNone},
        {"isZero", File_s_isZero, kJSPropertyAttributeNone},
        {0, 0, 0}
    };
    
    JSClassDefinition File_s_definition = {
        0,
        kJSClassAttributeNone,
        
        "File",
        NULL,
        
        NULL,
        staticFunctions,
        
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL
    };
    
    JSClassRef File = JSClassCreate(&File_s_definition);
    JSObjectRef FileObject = JSObjectMake(context, File, NULL);
    JSStringRef FileString = JSStringCreateWithUTF8CString("File");
    JSObjectSetProperty(context,
                        contextGlobalObject,
                        FileString,
                        FileObject,
                        kJSPropertyAttributeNone,
                        NULL);
    JSClassRelease(File);
    JSStringRelease(FileString);
}

JSValueRef File_s_atime(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeDate(context, fileStat.st_atimespec);
}

JSValueRef File_s_isBlockDev(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, S_ISBLK(fileStat.st_mode));
}

JSValueRef File_s_isCharDev(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, S_ISCHR(fileStat.st_mode));
}

JSValueRef File_s_chmod(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount < 2 ||
        (argumentCount >= 1 &&
         !JSValueIsNumber(context, arguments[0])))
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    mode_t mode = JSValueToNumber(context, arguments[0], NULL);
    unsigned int i = 1;
    unsigned int count = 0;
    
    for (i = 1; i < argumentCount; ++i)
    {
        char* fileName = UTF8CStringFromJSValueRef(context, arguments[i]);
        int returnValue = chmod(fileName, mode);
        free(fileName);
        
        if (!returnValue) { ++count; }
        else
        {
            *exception = JSValueMakeErrnoError(context, returnValue);
            return NULL;
        }
    }
    
    return JSValueMakeNumber(context, count);
}


//
// File.chown(ownerInt, groupInt, fileName, ...)
//
JSValueRef File_s_chown(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount < 3 ||
        (argumentCount >= 3 &&
         (!JSValueIsNumber(context, arguments[0]) || arguments[0] == NULL) &&
         (!JSValueIsNumber(context, arguments[1]) || arguments[1] == NULL)))
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    
    uid_t owner = (JSValueIsNumber(context, arguments[0])) ?
        JSValueToNumber(context, arguments[0], NULL) :
        -1;
    gid_t group = (JSValueIsNumber(context, arguments[1])) ?
        JSValueToNumber(context, arguments[1], NULL) :
        -1;
    
    unsigned int i = 2;
    unsigned int count = 0;
    
    for (i = 2; i < argumentCount; ++i)
    {
        char* fileName = UTF8CStringFromJSValueRef(context, arguments[i]);
        int returnValue = chown(fileName, owner, group);
        free(fileName);
        
        if (!returnValue) { ++count; }
        else
        {
            *exception = JSValueMakeErrnoError(context, returnValue);
            return NULL;
        }
    }
    
    return JSValueMakeNumber(context, count);
}


JSValueRef File_s_compare(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_ctime(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeDate(context, fileStat.st_ctimespec);
}


JSValueRef File_s_isDirectory(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, S_ISDIR(fileStat.st_mode));
}

//
// File.isExecutable(fileName)
//
JSValueRef File_s_isExecutable(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    // root can execute any file
    // owner can execute if the bit is set
    // group member can execute if the bit is set
    // all can execute if the bit is set
}

JSValueRef File_s_isExecutableReal(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_exists(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    return (returnValue) ?
        JSValueMakeBoolean(context, false) :
        JSValueMakeBoolean(context, true);
}

JSValueRef File_s_isFile(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, S_ISREG(fileStat.st_mode));
}

JSValueRef File_s_fileType(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = lstat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    char* fileType = NULL;
    
    if (S_ISREG(fileStat.st_mode)) { fileType = "file"; }
    else if (S_ISDIR(fileStat.st_mode)) { fileType = "directory"; }
    else if (S_ISCHR(fileStat.st_mode)) { fileType = "characterSpecial"; }
    else if (S_ISBLK(fileStat.st_mode)) { fileType = "blockSpecial"; }
    else if (S_ISFIFO(fileStat.st_mode)) { fileType = "fifo"; }
    else if (S_ISLNK(fileStat.st_mode)) { fileType = "link"; }
    else if (S_ISSOCK(fileStat.st_mode)) { fileType = "socket"; }
    else { fileType = "unknown"; }
    
    JSStringRef fileTypeString = JSStringCreateWithUTF8CString(fileType);
    JSValueRef fileTypeRef = JSValueMakeString(context, fileTypeString);
    JSStringRelease(fileTypeString);
    
    return fileTypeRef;
}

JSValueRef File_s_isGrpOwned(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
JSValueRef File_s_identical(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_link(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 2)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    
    char* oldName = UTF8CStringFromJSValueRef(context, arguments[0]);
    char* newName = UTF8CStringFromJSValueRef(context, arguments[1]);
    
    int returnValue = link(oldName, newName);
    free(oldName);
    free(newName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    return JSValueMakeUndefined(context);
}


JSValueRef File_s_makedirs(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
JSValueRef File_s_move(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 2)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    
    char* oldName = UTF8CStringFromJSValueRef(context, arguments[0]);
    char* newName = UTF8CStringFromJSValueRef(context, arguments[1]);
    
    int returnValue = rename(oldName, newName);
    free(oldName);
    free(newName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    return JSValueMakeUndefined(context);
}

JSValueRef File_s_mtime(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeDate(context, fileStat.st_mtimespec);
}

JSValueRef File_s_isOwned(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_isPipe(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, S_ISFIFO(fileStat.st_mode));
}

JSValueRef File_s_isReadable(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
JSValueRef File_s_isReadableReal(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
JSValueRef File_s_readlink(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
JSValueRef File_s_rename(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
JSValueRef File_s_safeUnlink(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_isSetGID(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, (fileStat.st_mode & S_ISGID));
}

JSValueRef File_s_isSetUID(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, (fileStat.st_mode & S_ISUID));
}

JSValueRef File_s_size(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeNumber(context, fileStat.st_size);
}

JSValueRef File_s_isSocket(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, S_ISSOCK(fileStat.st_mode));
}

JSValueRef File_s_stat(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_isSticky(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, (fileStat.st_mode & S_ISVTX));
}

JSValueRef File_s_symlink(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 2)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    
    char* oldName = UTF8CStringFromJSValueRef(context, arguments[0]);
    char* newName = UTF8CStringFromJSValueRef(context, arguments[1]);
    
    int returnValue = symlink(oldName, newName);
    free(oldName);
    free(newName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    return JSValueMakeUndefined(context);
}

JSValueRef File_s_isSymlink(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = lstat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, S_ISLNK(fileStat.st_mode));
}

JSValueRef File_s_syscopy(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_truncate(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 2)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    off_t offset = JSValueToNumber(context, arguments[1], exception);
    
    if (exception) { return NULL; }
    
    int returnValue = truncate(fileName, offset);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeUndefined(context);
}

JSValueRef File_s_umask(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
JSValueRef File_s_unlink(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_utime(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount < 3)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    
    struct timeval times[] = {
        JSValueToTimeval(context, arguments[0], exception),
        JSValueToTimeval(context, arguments[1], exception)
    };
    
    unsigned int i = 2;
    unsigned int count = 0;
    
    for (i = 2; i < argumentCount; ++i)
    {
        char* fileName = UTF8CStringFromJSValueRef(context, arguments[i]);
        int returnValue = utimes(fileName, times);
        free(fileName);
        
        if (!returnValue) { ++count; }
        else
        {
            *exception = JSValueMakeErrnoError(context, returnValue);
            return NULL;
        }
    }
    
    return JSValueMakeNumber(context, count);
}

JSValueRef File_s_isWritable(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
JSValueRef File_s_isWritableReal(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

JSValueRef File_s_isZero(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* fileName = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    struct stat64 fileStat;
    int returnValue = stat64(fileName, &fileStat);
    free(fileName);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeBoolean(context, (fileStat.st_size == 0));
}